#include "common.h"
#include "client.h"
#include <errno.h>

SSLClient::SSLClient(const char* keyfile, const char* passw)
{
  /* Build our SSL context*/
  ctx=initialize_ctx(keyfile,passw);
  ssl = NULL;
  sock = -1;
}

bool SSLClient::Connect(const char* host, int port)
{
  BIO *sbio;
  extern char *optarg;
  int c, err;

  /* Connect the TCP socket*/
  sock=tcp_connect(host,port);
  struct linger ling;
  ling.l_onoff = 0;
  ling.l_linger = 0;
  setsockopt(sock, SOL_SOCKET, SO_LINGER, &ling, sizeof(struct linger));

  /* Connect the SSL socket */
  ssl=SSL_new(ctx);
  sbio=BIO_new_socket(sock,BIO_NOCLOSE);
  SSL_set_bio(ssl,sbio,sbio);

  if((c=SSL_connect(ssl))<=0)
  {
    err = SSL_get_error(ssl,c);
    BIO_printf(bio_err, "host:%s, port: %d err:%d\n", host, port, err);

    if (err == SSL_ERROR_SYSCALL) perror("err:");
    berr_exit("SSL connect error");
  }
  check_cert(ssl,host);
}

int SSLClient::Read(void* buf, int len)
{
  return (SSL_read(ssl, buf, len));
}

int SSLClient::ReadUntilFull(void* buf, int len)
{
  return (SSL_read_until_full(ssl, buf, len));
}

int SSLClient::Write(const void* buf, int len)
{
  return SSL_write(ssl, buf, len);
}

SSLClient::~SSLClient() {
	SSL_shutdown(ssl);

	SSL_free(ssl);

	/* Shutdown the socket */
	destroy_ctx(ctx);

	if (sock >= 0)
		close(sock);
}

int tcp_connect(const char *host, int port)
  {
    struct hostent *hp;
    struct sockaddr_in addr;
    int sock;

    if(!(hp=gethostbyname(host)))
      berr_exit("Couldn't resolve host");
    memset(&addr,0,sizeof(addr));
    addr.sin_addr=*(struct in_addr*)
      hp->h_addr_list[0];
    addr.sin_family=AF_INET;
    addr.sin_port=htons(port);

    if((sock=socket(AF_INET,SOCK_STREAM,
      IPPROTO_TCP))<0)
      err_exit("Couldn't create socket");
    if(connect(sock,(struct sockaddr *)&addr,
      sizeof(addr))<0)
      err_exit("Couldn't connect socket");

    return sock;
  }

/* Check that the common name matches the
   host name*/
void check_cert( SSL *ssl, const char *host)
  {
    X509 *peer;
    char peer_CN[256];

    if(SSL_get_verify_result(ssl)!=X509_V_OK)
      berr_exit("Certificate doesn't verify");

    /*Check the cert chain. The chain length
      is automatically checked by OpenSSL when
      we set the verify depth in the ctx */

    /*Check the common name*/
    peer=SSL_get_peer_certificate(ssl);
    X509_NAME_get_text_by_NID(X509_get_subject_name(peer), NID_commonName, peer_CN, 256);

    if(strcasecmp(peer_CN,host))
    {
       err_exit("Common name doesn't match host name");
    }
  }

